package demo.java.math;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 原始数据
 * <p>
 * 原码： byte是一个字节保存的，有8个位，即8个0、1。 8位的第一个位是符号位， 也就是说0000 0001代表的是数字1 ,1000
 * 0000代表的就是-1 。
 * <p>
 * 所以正数最大位0111 1111，也就是数字127 ，负数最大为1111 1111，也就是数字-128。
 * <p>
 * 1、反码： 一个数如果是正，则它的反码与原码相同； 一个数如果是负，则符号位为1，其余各位是对原码取反；
 * <p>
 * 2、补码：利用溢出，我们可以将减法变成加法 对于十进制数，从9得到5可用减法： 9－4＝5,因为4+6＝10，我们可以将6作为4的补数。 改写为加法：
 * 9+6＝15（去掉高位1，也就是减10）得到5。 对于十六进制数，从c到5可用减法：c－7＝5，因为7+9＝16
 * 将9作为7的补数。改写为加法：c+9＝21（去掉高位1，也就是减16）得到5。
 * <p>
 * 在计算机中，如果我们用1个字节表示一个数，一个字节有8位，超过8位就进1，在内存中情况为（100000000），进位1被丢弃。
 *
 * <li>一个数为正，则它的原码、反码、补码相同
 * <li>一个数为负，刚符号位为1，其余各位是对原码取反，然后整个数加1
 *
 * <li>- 1的原码为 10000001
 * <li>- 1的反码为 11111110
 * <li>+ 1
 * <li>- 1的补码为 11111111
 *
 * <li>0的原码为 00000000
 * <li>0的反码为 11111111（正零和负零的反码相同）
 * <li>+1
 * <li>0的补码为 100000000（舍掉打头的1，正零和负零的补码相同）
 */
public class NumberDemo {

    private static Logger logger = LoggerFactory.getLogger(NumberDemo.class);

    static Random random = new Random();

    @Test
    public void testCase() {
        double pi = Math.PI;
        System.out.println(pi);
        System.out.println((long) pi);
        System.out.println((int) pi);
        System.out.println((float) pi);
    }


    /**
     * 四舍五入
     *
     * @param d
     * @param newScale 保留N位有效数字
     * @return
     */
    public static double scale(double d, int newScale) {
        return BigDecimal.valueOf(d).setScale(newScale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    @Test
    public void double2String() {
        double zero = 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014821969375;
        double pi = Math.PI;
        pi = pi / 10000;
        System.out.println(pi);
        System.out.println(Double.toString(pi));
        System.out.println(BigDecimal.valueOf(pi).toString());
        System.out.println(BigDecimal.valueOf(pi).toPlainString());
        System.out.println(String.valueOf(pi));
        System.out.println(String.format("%f", pi));
        System.out.println(scale(pi, 10));
        System.out.println(scale(zero, 10));
        System.out.println();

    }

    /**
     * <li>Double.valueOf,如果参数前后有空格，不影响
     */
    @Test
    public void doubleDemo() {
        int pi = (int) Math.PI;
        System.out.println("PI = " + Math.PI + " ~ " + pi);
        try {
            Double.valueOf(null);
        } catch (NullPointerException e) {
            System.err.println(e);
        }
        try {
            Double.valueOf("");
        } catch (NumberFormatException e) {
            System.err.println(e);
        }
        System.out.println(Double.NaN + " = " + String.format("%.20f", Double.NaN));
        System.out.println(Float.MAX_VALUE / 0.0);
        System.out.println(1.0 / 0.0);
        double d1 = 116.32318999999994;
        long l = (long) d1;
        int integer = (int) d1;
        logger.info("double: {} 转 long:{}, 转int= {}", d1, l, integer);
        Double dD = new Double(d1);
        l = ((Number) dD).longValue();
        logger.info("double: {} 转 long:{}", d1, l);
        System.out.println(Double.valueOf(d1).longValue());
        System.out.println(d1);
        System.out.println(Double.valueOf(" 128.36978      "));
        System.out.println(Double.toString(d1));
        System.out.println(Double.MAX_VALUE);
        System.out.println(BigDecimal.valueOf(Double.MAX_VALUE).toString());
        System.out.println(BigDecimal.valueOf(Double.MAX_VALUE).toPlainString());
        double a = Double.parseDouble(" 1255 ");
        System.out.println(a);
        double b = a / 100;
        System.out.println(b);
        System.out.println(Math.round(b));

        System.out.println((double) 50);


    }

    /**
     * <li>对NAN 进行比较都返回false;
     * <li>对NAN 进行计算都返回 NAN
     */
    @Test
    public void testNan() {

        System.out.println(Double.NaN > 2.0);
        System.out.println(Double.NaN == Double.NaN);
        System.out.println(Double.compare(Double.NaN, Double.NaN));
        System.out.println(Double.NaN + " = " + String.format("%.20f", Double.NaN));
        double nan = Double.NaN;
        System.out.println(100 > nan);
        System.out.println(100 < nan);
        System.out.println(nan > 0);
        System.out.println(nan + 1);
        System.out.println(nan * 0);
        System.out.println(nan / 0);
        System.out.println(nan == nan);
        System.out.println(Double.NaN == Double.NaN);

    }


    /**
     * <li>Double.valueOf,如果参数前后有空格，不影响
     */
    @Test
    public void testDouble() {
        boolean b = 1.0 == 1;
        System.out.println("1.0 == 1 " + b);

        b = 1.0 > 1;
        System.out.println("1.0 > 1 " + b);
    }

    @Test
    public void testLong() {
        System.out.println((Long) null);
        long now = System.currentTimeMillis();
        logger.info("long:{},double:{}", now, (double) now);
        double tmp = (double) now - 100L;
        logger.info("转double后减去100，结果为double类型：{}", tmp);
    }

    @Test
    public void demoInteger() {
        System.err.println(Integer.MAX_VALUE);
        int a = Integer.parseInt("8", 10);
        System.out.println(a);
        int b = Integer.parseInt("8");
        System.out.println(b);

        int i = 500;
        Integer integer1 = new Integer(i);
        Integer integer2 = new Integer(i);
        System.out.println(integer1 == integer2);
        System.out.println(i == integer1);
        System.out.println(integer1 == i);

        Number n = 0;
        System.out.println(n.doubleValue());
        AtomicInteger atomicInteger = new AtomicInteger(50);
    }

    @Test
    public void testToString() {
        Number d = 3.1415926D;
        System.out.println(d);
        Number i = 56;
        System.out.println(i);
        Number l = 79L;
        System.out.println(l);
    }

    /**
     * 常规的大字节序表示一个数的话，用高字节位的存放数字的低位，比较符合人的习惯。而小字节序和大字节序正好相反，用高字节位存放数字的高位。
     * <p>
     * 将int转成4字节的小字节序字节数组
     */
    public static byte[] toLittleEndian(int i) {
        byte[] bytes = new byte[4];
        bytes[0] = (byte) i;
        bytes[1] = (byte) (i >>> 8);
        bytes[2] = (byte) (i >>> 16);
        bytes[3] = (byte) (i >>> 24);
        return bytes;
    }

    /**
     * 常规的大字节序表示一个数的话，用高字节位的存放数字的低位，比较符合人的习惯。而小字节序和大字节序正好相反，用高字节位存放数字的高位。
     * <p>
     * 将小字节序的4字节的字节数组转成int
     */
    public static int getLittleEndianInt(byte[] bytes) {
        int b0 = bytes[0] & 0xFF;
        int b1 = bytes[1] & 0xFF;
        int b2 = bytes[2] & 0xFF;
        int b3 = bytes[3] & 0xFF;
        return b0 + (b1 << 8) + (b2 << 16) + (b3 << 24);
    }

    @Test
    public void testChar() {
        // Unicode for uppercase Greek omega character 'Ω'
        char uniChar = '\u03A9';
        System.out.println(uniChar);
        Character omega = 'Ω';
        System.out.println(omega);
    }

    /**
     * 零值判断
     */
    @Test
    public void testNull() {
        Integer i = 0;
        System.out.println(i == null);
    }

    /**
     * 零值判断
     */
    @Test
    public void testZero() {
        Integer i = 0;
        Float f = 0.0F;
        Double d = 0.0;
        System.out.println(i.doubleValue() == f.doubleValue());
        System.out.println(i.doubleValue() == d.doubleValue());
        System.out.println(f.doubleValue() == d.doubleValue());

    }

    public void testInt() {
        // 左移0位（不变）
        final int OP_READ = 1 << 0;
        System.out.println(OP_READ);
        // 左移2位（*4）
        final int OP_WRITE = 1 << 2;
        System.out.println(OP_WRITE);
        // 左移3位（*8）
        final int OP_CONNECT = 1 << 3;
        System.out.println(OP_CONNECT);
        // 左移4位（*16）
        final int OP_ACCEPT = 1 << 4;
        System.out.println(OP_ACCEPT);
        // -128 到 127 是Java常量池的常量，所以是一个对象
        Integer a = -128;
        Integer b = -128;
        System.out.println(a == b);
        // 超过这个范围就不是常量池的数据了，需要重新分配一个地址存放
        Integer c = 128;
        Integer d = 128;
        System.out.println(c == d);
    }

    public void testByte() {
        System.out.println(Byte.MAX_VALUE);
        System.out.println(Byte.MIN_VALUE);
        Byte a = 12;
        Byte b = 12;
        System.out.println(a == b);
        byte c = 12;
        System.out.println(c == a);
        Byte d = Byte.valueOf("12");
        System.out.println(a == d);
    }

    public static void unboxing() {

        Integer i = new Integer(-8);

        // 1. Unboxing through method invocation
        int absVal = absoluteValue(i);
        System.out.println("absolute value of " + i + " = " + absVal);

        List<Double> ld = new ArrayList<>();
        ld.add(3.1416); // Π is autoboxed through method invocation.

        // 2. Unboxing through assignment
        double pi = ld.get(0);
        System.out.println("pi = " + pi);

    }

    public static int absoluteValue(int i) {
        return (i < 0) ? -i : i;
    }

    /**
     * 二进制位运算
     */
    @Test
    public void bitDemo() {
        // The number 26, in binary
        int binVal = 0b10000000000000000000000000011010;
        System.out.println("binVal = " + Integer.toBinaryString(binVal));
        System.out.println("按位取反：" + "~binVal = " + Integer.toBinaryString(~binVal));
        System.out.println("左移：" + "(binVal<<2) = " + Integer.toBinaryString((binVal << 2)));
        System.out.println("右移：" + "(binVal>>2) = " + Integer.toBinaryString((binVal >> 2)));
        System.out.println("无符号右移：" + "(binVal>>>1) = " + Integer.toBinaryString((binVal >>> 1)));

        int bitmask2 = 0b100011;
        int bitmask1 = 0b111010;
        System.out.println("按位与：" + Integer.toBinaryString(bitmask2) + " & " + Integer.toBinaryString(bitmask1) + " = "
                + Integer.toBinaryString(bitmask2 & bitmask1));
        System.out.println("按位或：" + Integer.toBinaryString(bitmask2) + " | " + Integer.toBinaryString(bitmask1) + " = "
                + Integer.toBinaryString(bitmask2 | bitmask1));
        System.out.println("按位异或：" + Integer.toBinaryString(bitmask2) + " ^ " + Integer.toBinaryString(bitmask1) + " = "
                + Integer.toBinaryString(bitmask2 ^ bitmask1));
        System.out.println(Byte.toString((byte) 0b00010001));
    }

    public void prePostDemo() {
        int i = 3;
        i++;

        System.out.println(i);// prints 4
        ++i;

        System.out.println(i);// prints 5

        System.out.println(++i);// prints 6

        System.out.println(i++);// prints 6

        System.out.println(i);// prints 7

        int j = 0;
        j = (i++);// 相当于 a = i; i=i+1;
        j = i++;// 相当于 a = i; i=i+1;
        j = ++i;// 相当于 i=i+1; a = i; （先i = i + 1,再使用i的值）

        System.out.println(i);
        System.out.println(j);

        int n = i++ % 5;// n = 0
        System.out.println(n);

        double aValue = 8933.234;
        aValue++;
        System.out.println(aValue);
    }

    public static void demo() {
        boolean result = true;
        char capitalC = 'C';
        // Unicode for uppercase Greek omega character
        char uniChar = '\u03A9';// Ω
        byte b = 100;
        short s = 10000;
        int i = 100000;
        // The number 26, in decimal
        int decVal = 26;
        // The number 26, in hexadecimal
        int hexVal = 0x1a;
        // The number 26, in binary
        int binVal = 0b11010;

        double d1 = 123.4;
        // same value as d1, but in scientific notation
        double d2 = 1.234e2;
        double d3 = 123.4D;
        float f1 = 123.4F;

        long creditCardNumber = 1234_5678_9012_3456L;
        long socialSecurityNumber = 999_99_9999L;
        float pi = 3.14_15F;
        long hexBytes = 0xFF_EC_DE_5E;
        long hexWords = 0xCAFE_BABE;
        long maxLong = 0x7fff_ffff_ffff_ffffL;
        byte nybbles = 0b0010_0101;
        long bytes = 0b11010010_01101001_10010100_10010010;

        // Invalid: cannot put underscores adjacent to a decimal point
        /* float pi1=3_.1415F; */
        // Invalid: cannot put underscores adjacent to a decimal point
        /* float pi2=3._ 1415F; */
        // Invalid: cannot put underscores prior to an L suffix
        /* long socialSecurityNumber1=999_99_9999_ L; */

        // OK (decimal literal)
        int x1 = 5_2;
        // Invalid: cannot put underscores At the end of a literal
        // int x2=52_;
        // OK (decimal literal)
        int x3 = 5_______2;

        // Invalid: cannot put underscores in the 0x radix prefix
        /* int x4=0_ x52; */
        // Invalid: cannot put underscores at the beginning of a number
        /* int x5=0x_ 52; */
        // OK (hexadecimal literal)
        int x6 = 0x5_2;
        // Invalid: cannot put underscores at the end of a number
        /* int x7=0x52_; */
    }

    public void arithmeticDemo() {

        int result = 1 + 2;
        // result is now 3
        System.out.println("1 + 2 = " + result);
        int original_result = result;

        result = result - 1;
        // result is now 2
        System.out.println(original_result + " - 1 = " + result);
        original_result = result;

        result = result * 2;
        // result is now 4
        System.out.println(original_result + " * 2 = " + result);
        original_result = result;

        result = result / 2;
        // result is now 2
        System.out.println(original_result + " / 2 = " + result);
        original_result = result;

        result = result + 8;
        // result is now 10
        System.out.println(original_result + " + 8 = " + result);
        original_result = result;

        result = result % 7;
        // result is now 3
        System.out.println(original_result + " % 7 = " + result);
    }

    /**
     * rint 四舍五入，返回double值 注意.5的时候会取偶数
     */
    static void demoRint() {
        System.out.println(Math.rint(10.1)); // 10.0
        System.out.println(Math.rint(10.7)); // 11.0
        System.out.println(Math.rint(11.5)); // 12.0
        System.out.println(Math.rint(10.5)); // 10.0
        System.out.println(Math.rint(10.51)); // 11.0
        System.out.println(Math.rint(-10.5)); // -10.0
        System.out.println(Math.rint(-11.5)); // -12.0
        System.out.println(Math.rint(-10.51)); // -11.0
        System.out.println(Math.rint(-10.6)); // -11.0
        System.out.println(Math.rint(-10.2)); // -10.0
    }

    /**
     * round 四舍五入，float时返回int值，double时返回long值
     */
    @Test
    public void round() {
        System.out.println(Math.round(10.1)); // 10
        System.out.println(Math.round(10.5)); // 11
        System.out.println(Math.round(-10.5)); // -10
        System.out.println(Math.round(-10.51)); // -11
        System.out.println(Math.round(-10.6)); // -11

        double d = 114.145;
        d = (double) Math.round(d * 100) / 100;
        System.out.println(d);
    }

    /**
     * abs求绝对值
     */
    static void demoAbs() {
        System.out.println(Math.abs(-10.4)); // 10.4
        System.out.println(Math.abs(10.1)); // 10.1
    }

    /**
     * ceil天花板的意思，就是返回大的值，注意一些特殊值
     */
    static void demoCeil() {
        System.out.println(Math.ceil(-10.1)); // -10.0
        System.out.println(Math.ceil(10.7)); // 11.0
        System.out.println(Math.ceil(-0.7)); // -0.0
        System.out.println(Math.ceil(0.0)); // 0.0
        System.out.println(Math.ceil(-0.0)); // -0.0

    }

    /**
     * floor地板的意思，就是返回小的值
     */
    static void floor() {
        System.out.println(Math.floor(-10.1)); // -11.0
        System.out.println(Math.floor(10.7)); // 10.0
        System.out.println(Math.floor(-0.7)); // -1.0
        System.out.println(Math.floor(0.0)); // 0.0
        System.out.println(Math.floor(-0.0)); // -0.0
    }

    /**
     * max 两个中返回大的值,min和它相反，就不举例了
     */
    static void max() {
        System.out.println(Math.max(-10.1, -10)); // -10.0
        System.out.println(Math.max(10.7, 10)); // 10.7
        System.out.println(Math.max(0.0, -0.0)); // 0.0
    }

    /**
     * random 取得一个大于或者等于0.0小于不等于1.0的随机数
     */
    static void demoRandom() {
        System.out.println(Math.random()); // 0.08417657924317234
        System.out.println(Math.random()); // 0.43527904004403717
    }

    /**
     * Double类型求和
     *
     * @param d
     * @return
     */
    public static Double sum(Double... d) {
        Double sum = 0.0;
        for (int i = 0; i < d.length; i++) {
            if (d[i] == null) {
                sum += 0.0;
            } else {
                sum += d[i];
            }
        }
        return sum;
    }

    /**
     * 计算比例
     *
     * @param numerator   分子
     * @param denominator 分母
     * @return
     */
    private double processProportion(Number numerator, Number denominator) {
        return numerator.doubleValue() / denominator.longValue();
    }

    // 定义一个很小的误差范围，用于近似相等的判断
    private static final float EPSILON = 0.00001f;

    public static boolean areEqual(Float f1, Float f2) {
        // 如果两个对象都为null，则认为它们相等
        if (f1 == null && f2 == null) {
            return true;
        }

        // 如果只有一个对象为null，则它们不相等
        if (f1 == null || f2 == null) {
            return false;
        }

        // 处理特殊值
        if (Float.isNaN(f1) && Float.isNaN(f2)) {
            return true;  // 两个NaN被认为是相等的
        }
        if (Float.isInfinite(f1) && Float.isInfinite(f2)) {
            return f1.equals(f2);  // 正无穷大等于正无穷大，负无穷大等于负无穷大
        }

        // 对于普通的浮点数，使用近似相等的判断
        return Math.abs(f1 - f2) < EPSILON;
    }

    @Test
    public void testFloatEquals() {
        // 测试用例
        System.out.println(areEqual(1.0f, 1.0f));  // true
        System.out.println(areEqual(1.0f, 1.000001f));  // true
        System.out.println(areEqual(1.0f, 1.1f));  // false
        System.out.println(areEqual(null, null));  // true
        System.out.println(areEqual(1.0f, null));  // false
        System.out.println(areEqual(Float.NaN, Float.NaN));  // true
        System.out.println(areEqual(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY));  // true
        System.out.println(areEqual(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY));  // false
    }


    @Test
    public void testFloat() {
        String s = "116.32318999999994";
        float f1 = (float) 116.32318999999994;
        System.out.println(f1);
        System.out.println(Float.valueOf(s));

        System.out.println(Float.MIN_VALUE + " = " + String.format("%.200f", Float.MIN_VALUE));
        System.out.println(Float.MAX_VALUE + " = " + String.format("%f", Float.MAX_VALUE));
        System.out.println(0.000000000000000000000000000000000000000000001 < Float.MIN_VALUE);
        System.out.println(0 > Float.MIN_VALUE);
    }
}
